#include "decoder.h"
#include "util.h"
#include <fmod.hpp>
#include <fmod_errors.h>
// #include "bass.h"
#include <string>
#include <cstdint>
#include <stdexcept>

using namespace std;
using namespace FMOD;


vector<float> decodeFmod(const char * filename, unsigned & sampleRate) {
    const struct FmodSystem {
        System * sys;
        FmodSystem() : sys(nullptr) {
            System_Create(&sys);
            sys->setOutput(FMOD_OUTPUTTYPE_NOSOUND_NRT);
            sys->init(128, FMOD_INIT_STREAM_FROM_UPDATE, 0);
        }
        ~FmodSystem() { if (sys != nullptr) { sys->release(); } }
        operator System *() const { return sys; }
        System * operator ->() const { return sys; }
    } sys;

    const struct FmodStream {
        Sound * sound;
        FmodStream(System * sys, const char * filename) : sound(nullptr) {
            FMOD_RESULT ret = sys->createStream(filename, FMOD_OPENONLY | FMOD_ACCURATETIME | FMOD_MPEGSEARCH|FMOD_IGNORETAGS, 0, &sound);
            if (ret != FMOD_OK) {
                throw runtime_error(string("Failed to decode with FMOD: ") + FMOD_ErrorString(ret));
            }
        }
        ~FmodStream() { if (sound != nullptr) { sound->release(); } }
        operator Sound *() const { return sound; }
        Sound * operator ->() const { return sound; }
    } sound(sys, filename);

    float freq;
    FMOD_SOUND_FORMAT format;
    int channels;
    int bits;
    unsigned samples;
    sound->getDefaults(&freq, 0);
    sound->getFormat(0, &format, &channels, &bits);
    sound->getLength(&samples, FMOD_TIMEUNIT_PCM);
    sampleRate = static_cast<unsigned int>(freq);

    string x(samples * channels * (bits / 8), '\0');
    unsigned read;
    sound->readData(&x[0], x.size(), &read);
    if (read != x.size()) {
        x.resize(read);
    }
    switch (format) {
    case FMOD_SOUND_FORMAT_PCM16:
        return convertToMono(x, SampleFormat::Signed16LE, channels);
    case FMOD_SOUND_FORMAT_PCM24:
        return convertToMono(x, SampleFormat::Signed24LE, channels);
    case FMOD_SOUND_FORMAT_PCMFLOAT:
        return convertToMono(x, SampleFormat::Float32LE, channels);
    default:
        throw runtime_error("Unexpected sample format from FMOD");
    }
}
